/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) Transformation.java 2018-08-06 17:26
 */

package cn.jh.common.core.crypto;

import static com.google.common.base.Preconditions.checkArgument;


/**
 * Jca Cipher transformation string to mapping module.
 *
 * @author Fuchun
 * @since 1.0
 */
public class Transformation {

    public static Transformation pbeOf(String algorithm) {
        return of(algorithm, OprMode.CBC, PaddingSchema.PKCS5);
    }

    public static Transformation rsaOf(PaddingSchema padding) {
        return rsaOf(OprMode.ECB, padding);
    }

    public static Transformation rsaOf(OprMode mode, PaddingSchema padding) {
        // RSA 算法模式中，只推荐 ECB，其他的均已不再推荐使用。
        return of("RSA",
                mode == null ? OprMode.ECB : mode,
                padding == null ? PaddingSchema.PKCS1 : padding);
    }

    public static Transformation aesOf(PaddingSchema padding) {
        return aesOf(OprMode.CBC, padding);
    }

    public static Transformation aesOf(OprMode mode, PaddingSchema padding) {
        // AES 算法模式中，只推荐 CBC，其他的均已不再推荐使用。
        return of("AES", mode,
                padding == null ? PaddingSchema.PKCS5 : padding);
    }

    public static Transformation tripleDes() {
        return tripleDesOf(OprMode.ECB, PaddingSchema.PKCS5);
    }

    public static Transformation tripleDesOf(OprMode mode, PaddingSchema padding) {
        // TripleDES (DESede)中，只推荐 ECB
        return of("DESede",
                mode == null ? OprMode.ECB : mode,
                padding == null ? PaddingSchema.PKCS5 : padding);
    }

    public static Transformation of(String algorithm, OprMode mode, PaddingSchema padding) {
        return new Transformation(algorithm, mode, padding);
    }

    @SuppressWarnings("StringSplitter")
    public static Transformation forString(String input) {
        if (!input.contains("/")) {
            return new Transformation(input, null, null);
        }
        String[] parts = input.split("/");
        String algorithm = parts[0].trim();
        checkArgument(algorithm.length() > 0, "The given algorithm (\"%s\") is invalid", algorithm);
        checkArgument(parts.length == 3,"The given input \"%s\" is not a transformation.", input);
        OprMode mode = OprMode.forString(parts[1]);
        checkArgument(mode != OprMode.NONE, "Not support \"%s\" mode of operation.", mode);
        PaddingSchema padding = PaddingSchema.forString(parts[2]);
        return new Transformation(algorithm, mode, padding);
    }

    private final String algorithm;
    // 分组模式。默认：`CBC`
    private final OprMode mode;
    // 填充（密码学）模式。默认：`PKCS5Padding`
    private final PaddingSchema padding;

    private final transient String transformationString;

    private Transformation(String algorithm, OprMode mode, PaddingSchema padding) {
        this.algorithm = algorithm;
        this.mode = mode == null ? OprMode.CBC : mode;
        this.padding = padding == null ? PaddingSchema.NONE : padding;

        transformationString = String.join("/", algorithm, this.mode.name(), this.padding.text);
    }

    public String getAlgorithm() {
        return algorithm;
    }

    public OprMode getMode() {
        return mode;
    }

    public PaddingSchema getPadding() {
        return padding;
    }

    @Override
    public String toString() {
        return transformationString;
    }
}
